/*
* Copyright © Shanghai Awinic Technology Co., Ltd. 2020-2020. All rights reserved.
* Description: I2C driver related library function files.
* Date： 20201126
* Awinic_Version： aw_soc_driver_v1.0.0
*/
#include "compile_option.h"
#ifdef AW_86802

#include "aw8680x_global.h"

#ifdef I2C_TEST
	I2C_STATUS_TYPE_E g_i2c_status = I2C_NOEVENT;
	AW_U8 g_i2c_rx_buff[I2C_BUFF_LEN] = {0};	// i2c receive buff
	AW_U8 g_i2c_tx_buff[I2C_BUFF_LEN] = {0};	// i2c send buff
	AW_U8 g_i2c_data_cent = AW_NUM_NULL; // Used to send and receive counts.
#endif
/**
  * @brief  I2C interrupt number configuration function.
  * @param  i2c_irq_pri_e is used to set interrupt priority
  * @retval None
  */
static void i2c_irq_config(IRQ_PRI_E i2c_irq_pri_e)
{
	NVIC_EnableIRQ(IRQ_IIC0); // Enable Interrupt
	NVIC_SetPriority(IRQ_IIC0, i2c_irq_pri_e); // Set Interrupt Priority.
}

/**
  * @brief  I2C master mode configuration function.
  * @param  I2C pointer to a I2C_HANDLE_TYPE_S structure that contains
  *                the configuration information for the specified I2C.
  * @retval RET_STATUS_E status
  */
static RET_STATUS_E i2c_master_mode(I2C_HANDLE_TYPE_S *p_i2c_lib)
{
	/* Check the I2C handle allocation. */
	if (p_i2c_lib == AW_NULL) {
		return RET_ERROR;
	}

	/* I2C master mode config. */
	I2C0->CONTROL = I2C_CLEAN_DATA; // Clean CON register data.
	I2C0->CONTROL |= I2C_SLAVE_DIS; // Disanbling I2C slave mode.
	I2C0->CONTROL |= I2C_MASTER_EN; // Enabling I2C master mode.
	I2C0->CONTROL |= p_i2c_lib->init_s.i2c_speed_e; // I2C speed mode
	I2C0->CONTROL |= I2C_RESTART_EN; // Enabling send restart signal.
	I2C0->TAR_ADDR = p_i2c_lib->init_s.i2c_addr; // Write I2C address.

	return RET_OK;
}

/**
  * @brief  I2C interrupt mask initialize configuration function
  * @param  None
  * @retval None
  */
static void i2c_mask_init(void)
{
	AW_U32 tmp_flag = AW_NUM_NULL;

	tmp_flag = I2CIM_STOP | I2CIM_RX_FULL | I2CIM_RD_REQ | I2CIM_START | I2CIM_TX_ABRT | I2CIM_RX_DONE;
	I2C_SET_MASK(tmp_flag);
}

/**
  * @brief  I2C slave mode configuration function
  * @param  I2C pointer to a I2C_HANDLE_TYPE_S structure that contains
  *                the configuration information for the specified I2C.
  * @retval RET_STATUS_E status
  */
static RET_STATUS_E i2c_slave_mode(I2C_HANDLE_TYPE_S *p_i2c_lib)
{
	/* Check the I2C handle allocation */
	if (p_i2c_lib == AW_NULL) {
		return RET_ERROR;
	}

	/* I2C slave mode config */
	i2c_irq_config(p_i2c_lib->i2c_irq_pri_e); // Config I2C interrupt number

	I2C0->CONTROL = I2C_CLEAN_DATA; // Clean CON register data
	I2C0->CONTROL &= I2C_SLAVE_EN; // Enabling I2C slave mode
	I2C0->CONTROL &= I2C_MASTER_DIS; // Disanbling I2C master mode
	I2C0->CONTROL |= p_i2c_lib->init_s.i2c_speed_e; // I2C speed mode
	I2C0->CONTROL |= I2C_RESTART_EN; // Enabling send restart signal
	I2C0->SLV_ADDR = p_i2c_lib->init_s.i2c_addr; // write I2C address

	i2c_mask_init(); // Set I2C intterrupt mask.

	return RET_OK;
}

/**
  * @brief  Initializes the i2c according to the specified parameters
  *         in the I2C_HANDLE_TYPE_S and initialize the associated handle.
  * @param  p_i2c_lib pointer to a I2C_HANDLE_TYPE_S structure that contains
  *                the configuration information for the specified I2C.
  * @retval RET_STATUS_E status
  */
RET_STATUS_E i2c_init(I2C_HANDLE_TYPE_S *p_i2c_lib)
{
	AW_U32 tmp_flag = AW_NUM_NULL; // Use clean interrupt state
	RET_STATUS_E aw_i2c_status = RET_OK;

	(void) tmp_flag; // Prevent warnings in the compiler

	/* Check the I2C handle allocation */
	if (p_i2c_lib == AW_NULL) {
		return RET_ERROR;
	}

	/* Check the parameters */
	assert_param(I2C_CHECK_MODE(p_i2c_lib->mode_e));
	assert_param(I2C_CHECK_ADDR(p_i2c_lib->init_s.i2c_addr));
	assert_param(I2C_CHECK_SPEED(p_i2c_lib->init_s.i2c_speed_e));
	assert_param(I2C_CHECK_RX_TL(p_i2c_lib->init_s.fifo_rx_tl));
	assert_param(I2C_CHECK_RX_TL(p_i2c_lib->init_s.fifo_tx_tl));
	assert_param(I2C_CHECK_SDA_SETUP(p_i2c_lib->init_s.sda_setup));
	assert_param(I2C_CHECK_FS_SPKLEN(p_i2c_lib->init_s.fs_spklen));
	assert_param(I2C_CHECK_FS_SCL_HCNT(p_i2c_lib->init_s.fs_scl_hcnt));
	assert_param(I2C_CHECK_FS_SCL_LCNT(p_i2c_lib->init_s.fs_scl_lcnt));
	assert_param(I2C_CHECK_SDA_HOLD(p_i2c_lib->init_s.sda_hold));

	/* Configure I2C clock */
	i2c_clk_init();

	/* IO pinmux is configured as I2C mode */
	i2c_pmux_init();

	/* Register function configuration */
	I2C0->ENABLE = I2C_CLEAN_DATA; // Clean enable register data
	I2C0->INT_FLAG = I2C_CLEAN_DATA; // Close all interrupt
	tmp_flag = I2C0->CLR_INT; // Clean I2C all interrupt state

	/* Judge I2C mode */
	if (p_i2c_lib->mode_e == I2C_MODE_MASTER) {
		aw_i2c_status = i2c_master_mode(p_i2c_lib);
	} else if (p_i2c_lib->mode_e == I2C_MODE_SLAVE) {
		aw_i2c_status = i2c_slave_mode(p_i2c_lib);
	}

	if (p_i2c_lib->init_s.i2c_speed_e == I2C_STAND_MODE) {
		gpio_speed_fast(~(GPIO_PIN_12 | GPIO_PIN_13));
		I2C0->ST_SCLH_CNT = p_i2c_lib->init_s.scl_hcnt;
		I2C0->ST_SCLL_CNT = p_i2c_lib->init_s.scl_lcnt;
	} else if (p_i2c_lib->init_s.i2c_speed_e == I2C_FAST_MODE) {
		gpio_speed_fast(GPIO_PIN_12 | GPIO_PIN_13);
		I2C0->FS_SPK_LIMIT = p_i2c_lib->init_s.spklen;
		I2C0->FT_SCLH_CNT = p_i2c_lib->init_s.scl_hcnt;
		I2C0->FT_SCLL_CNT = p_i2c_lib->init_s.scl_lcnt;
	}

	I2C0->SDA_HOLD_TIME = p_i2c_lib->init_s.sda_hold;
	I2C0->SDA_SETUP_TIME = p_i2c_lib->init_s.sda_setup;
	I2C0->RX_FIFO_TH = p_i2c_lib->init_s.fifo_rx_tl;
	I2C0->TX_FIFO_TH = p_i2c_lib->init_s.fifo_tx_tl;

	I2C0->ENABLE = I2C_ENABLE_EN; //Enabling I2C

	return aw_i2c_status;
}

/**
  * @brief  I2C get cmd data function
  * @param  None
  * @retval AW_U8 cmd_data
  */
static AW_U8 i2c_get_cmd_data(void)
{
	AW_U8 cmd_data = AW_NUM_NULL;

	cmd_data = (AW_U8)I2C0->DA_BUF_CMD;

	return cmd_data;
}

/**
  * @brief  I2C stop function
  * @param  None
  * @retval RET_STATUS_E status
  */
static RET_STATUS_E i2c_wait_stop(void)
{
	AW_S32 i2c_timeout = I2C_TIMEOUT;
	while ((I2C0->RAW_INT_STATUS & I2CIM_STOP) != I2CIM_STOP) {
		if((i2c_timeout--) <= 0) {
			return RET_ERROR;
		}
	}
	return RET_OK;
}

/**
  * @brief  I2C read data function
  * @param  read_len (read data lenth), p_rx_buff(data), I2C(Corresponding peripheral)
  * @retval RET_STATUS_E status
  */
RET_STATUS_E i2c_read_bytes(AW_U16 read_len, AW_U8 *p_rx_buff)
{
	AW_U32 wr_cnt = AW_NUM_NULL;
	AW_U32 i = AW_NUM_NULL;
	AW_U32 tmp = AW_NUM_NULL;

	(void) tmp;

	/* Check whether p_rx_buff meets the requirements */
	if (p_rx_buff == AW_NULL) {
		return RET_ERROR;
	}

	/* Clear flag */
	tmp = I2C0->CLR_STOP_DET_INT;
	tmp = I2C0->CLR_TX_ABRT_INT;

	for (i = AW_NUM_NULL; i < read_len; i++) {
		while ((wr_cnt < read_len) &&
			((I2C0->STATUS & I2C_STATUS_TFNF) == I2C_STATUS_TFNF)) {
			I2C_SET_CMD_MODE(I2C_CMD_RX_MODE);
			wr_cnt++;
		}
		while ((I2C0->STATUS & I2C_STATUS_RFNE) != I2C_STATUS_RFNE) {
		}
		p_rx_buff[i] = i2c_get_cmd_data();
	}
	i2c_wait_stop();

	return RET_OK;
}

/**
  * @brief  I2C write data function
  * @param  write_len (write data lenth),p_rx_buff(data),i2c(Corresponding peripheral)
  * @retval RET_STATUS_E status
  */
RET_STATUS_E i2c_write_bytes(AW_U16 write_len, AW_U8 *p_tx_buff)
{
	AW_U32 i = AW_NUM_NULL;
	AW_U32 tmp = AW_NUM_NULL;

	(void) tmp;

	/* Check whether p_tx_buff meets the requirements */
	if (p_tx_buff == AW_NULL) {
		return RET_ERROR;
	}

	/* clear flag */
	tmp = I2C0->CLR_STOP_DET_INT;
	tmp = I2C0->CLR_TX_ABRT_INT;

	for (i = AW_NUM_NULL; i < write_len; i++) {
		while ((I2C0->STATUS & I2C_STATUS_TFNF) != I2C_STATUS_TFNF) {
		}
		I2C_SET_CMD_MODE(p_tx_buff[i]);
	}
	i2c_wait_stop();

	return RET_OK;
}

/**
  * @brief  I2C callback function
  * @param  None
  * @retval None
  */
__weak void i2c_irq(void)
{
	/* NOTE : This function Should not be modified, when the i2c_irq is needed,
			the aw_lib_timer_call could be implemented in the user file.
	*/
#ifdef I2C_TEST
	AW_U8 i2c_cmd_data = AW_NUM_NULL;
	AW_U32 i2c_flag = AW_NUM_NULL;
	AW_U32 i2c_event_flag = I2C_NOEVENT;
	AW_U32 i2c_status = I2C0->INT_STATUS; /* Get interrupt status */

	(void)i2c_flag;
	(void)i2c_cmd_data;

	/* Start signal interruption. */
	if ((i2c_status & I2C_START_DET) == I2C_START_DET) {
		i2c_flag = I2C0->CLR_STAR_DET_INT;

		g_i2c_data_cent = AW_NUM_NULL;
		g_i2c_status = I2C_STAR;
	}

	/* Master write data interruption,and the slave receives the data from the master. */
	if ((i2c_status & I2C_RX_FULL) == I2C_RX_FULL) {
		if((I2C0->STATUS & I2C_STATUS_RFNE) == I2C_STATUS_RFNE) { // FIFO has data.
			i2c_cmd_data = (AW_U8)I2C0->DA_BUF_CMD;
			g_i2c_rx_buff[g_i2c_data_cent] = i2c_cmd_data; // Put the received data into the buffer buf.
			g_i2c_data_cent++;
			if (g_i2c_data_cent >= I2C_BUFF_LEN) {
				g_i2c_data_cent = 0;
			}
			g_i2c_status = I2C_RX_BUSY; // Slave is busy for receive data.
		}
	}

	/* Master read data interruption, and the slave sends data and the master receives data. */
	if ((i2c_status & I2C_RD_REQ) == I2C_RD_REQ) {
		i2c_flag = I2C0->CLR_TX_ABRT_INT; // Release FIFO.
		g_i2c_data_cent = AW_NUM_NULL;

		i2c_cmd_data = g_i2c_tx_buff[g_i2c_data_cent];
		I2C0->DA_BUF_CMD = i2c_cmd_data;

		g_i2c_data_cent++;
		I2C_SET_MASK(I2CIM_TX_EMPTY); // Enable txEmpty interrupt.
		i2c_flag = I2C0->CLR_RD_REQ_INT; // Clear CLR_RD_REQ flag.
		g_i2c_status = I2C_TX_BUSY; // Slave is busy for send data.
	}

	if (i2c_status & I2CIM_TX_EMPTY) { // TX fifo empty.
		i2c_flag = I2C0->CLR_TX_ABRT_INT;

		i2c_cmd_data = g_i2c_tx_buff[g_i2c_data_cent];
		I2C0->DA_BUF_CMD = i2c_cmd_data;

		g_i2c_data_cent++;
		if (g_i2c_data_cent >= I2C_BUFF_LEN) {
			g_i2c_data_cent = 0;
		}

		g_i2c_status = I2C_TX_BUSY; // Slave is busy for send data.
	}

	if (i2c_status & I2CIM_TX_ABRT) { // Send fail clean interrupt.
		i2c_flag = I2C0->CLR_TX_ABRT_INT;
	}

	if (i2c_status & I2CIM_RX_DONE) { // RD done.
		// Clean rx done interrupt.
		i2c_flag = I2C0->CLR_RX_DONE_INT;
		// Send done, disable txEmpty.
		I2C_CLEAN_MASK(I2CIM_TX_EMPTY); // Disable txEmpty interrupt.

		g_i2c_status = I2C_TX_BUSY; // Slave is busy for send data.
	}

	/* Check Stop event happen. */
	if ((i2c_status & I2C_STOP_DE) == I2C_STOP_DE) {
		i2c_flag = I2C0->CLR_STOP_DET_INT; // Clear the STOP interrupt Flag.
		i2c_flag = I2C0->CLR_INT;
		i2c_event_flag = g_i2c_status;
		switch (i2c_event_flag) {
			case I2C_RX_BUSY: // Slave receive status Stop flag.
				g_i2c_status = I2C_RX_END;
				break;

			case I2C_TX_BUSY: // Slave send stop.
				I2C0->INT_FLAG &= ~I2CIM_TX_EMPTY;
				g_i2c_status = I2C_TX_END;
				break;

			default:
				i2c_flag = I2C0->CLR_STOP_DET_INT; // Clear the STOP interrupt Flag.
				i2c_flag = I2C0->CLR_INT;
				g_i2c_status = I2C_NOEVENT;
				break;
		}
	}
#endif // I2C_TEST
}
#endif
